Severstal is leading the charge in efficient steel mining and production. With over 50K employees, 5K clients, and 25K products, this company has produced 11.8 Million tonnes of steel, 4.7 Million tonnes of coal and 11 Million tonnes of iron core pellets in 2019 which justifies its dominance in the steel industry.
We know that steel is one of the most important and most widely used building materials of modern times due to its impressive properties like durability, thermal conductivity, resistance to natural and man-made wear and most importantly it is resistant to corrosion which makes the material ubiquitous around the world. In order to make steel production more efficient without compromising the quality, Severstal wants to leverage the advances of Artificial Intelligence like computer vision to identify defects in delicate flat steel sheets. These sheets are produced from a sequence of manufacturing processes that involves heating, rolling, drying and cutting
Off lately, Severstal is using images generated form high frequency cameras to power a defect detection algorithm and Severstal is expecting the AI engineers to improve the algorithm to detect defects with high precision.
Credits: Kaggle
Problem Statement
The data folder (size 2GB in total) provided by Severstal contains the following:
Sample train image
train_csv data point
import warnings
warnings.filterwarnings("ignore")
from datetime import datetime
import pandas as pd
import numpy as np
from numpy import asarray
import matplotlib.pyplot as plt
%matplotlib notebook
%matplotlib inline
import seaborn as sns
from sklearn import metrics
import pickle
from tqdm import tqdm
import math
import random
import os
import cv2
from collections import Counter
from os import listdir
from matplotlib import image
from matplotlib import pyplot
from PIL import Image
from collections import defaultdict
from sklearn.model_selection import train_test_split
import tensorflow as tf
from keras import backend as K
from keras.losses import binary_crossentropy
from keras.layers import UpSampling2D, Conv2D, Activation, LeakyReLU, BatchNormalization,Input,Conv2DTranspose,Dropout
from keras.layers.pooling import MaxPooling2D, GlobalMaxPool2D
from keras import Model
from keras.preprocessing.image import ImageDataGenerator,array_to_img, img_to_array, load_img
from keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint, LearningRateScheduler,Callback
from keras.optimizers import Adam
from tqdm import tqdm_notebook
from keras.layers.pooling import MaxPooling2D
from keras.layers.merge import concatenate,add
#loading the train csv file containing pixels indicating defects
train_df= pd.read_csv("/content/train.csv")
train_df.head()
EncodedPixels columns indicate run-length encoding on the pixel values which are pairs of values that contain a start position and a run length. E.g. '1 3' implies starting at pixel 1 and running a total of 3 pixels (1,2,3).
The competition format requires a space delimited list of pairs. For example, '1 3 10 5' implies pixels 1,2,3,10,11,12,13,14 are to be included in the mask. The metric checks that the pairs are sorted, positive, and the decoded pixel values are not duplicated. The pixels are numbered from top to bottom, then left to right: 1 is pixel (1,1), 2 is pixel (2,1), etc.
print(train_df.shape)
# train data
nan_rows = train_df[train_df.isnull().any(axis=1)]
nan_rows
train_count= 0
test_count= 0
for filename in listdir('train_images'):
#counting number of images in train folder
train_count+=1
for filename in listdir('test_images'):
#counting number of images in test folder
test_count+=1
print("Number of images in the train folder: ",train_count)
print("Number of images in the test folder: ",test_count)
print('-'*100)
#Pie-chart https://pythonspot.com/matplotlib-pie-chart/
# Data to plot
labels = 'Test', 'Train'
sizes = [test_count,train_count]
colors = ['red','lightskyblue']
explode = (0.1, 0) # explode 1st slice
# Plot
plt.figure(figsize=(7,7))
plt.pie(sizes, explode=explode, labels=labels, colors=colors,
autopct='%1.1f%%', shadow=True, startangle=140)
plt.axis('equal')
plt.show()
print("Number of images in the train folder which do not have defects are:",(train_count-train_df.shape[0]))
# load all images in a directory
#from os import listdir
from matplotlib import image
# load all images in a directory
loaded_images = list()
defects= list(train_df.ImageId.values)
for filename in listdir('train_images'):
if str(filename) not in defects:
# load image
img_data = image.imread('train_images/' + filename)
# store loaded image
loaded_images.append(img_data)
from matplotlib import pyplot
for i in range(5):
disp= loaded_images[i]
pyplot.imshow(disp)
pyplot.show()
from PIL import Image
# load the image
image = Image.open('0a5a82b86.jpg')
# summarize some details about the image
print(image.format)
print(image.mode)
print(image.size)
# show the image
image.show()
from PIL import Image
image_size=[]
for image_id in listdir('train_images'):
img=Image.open("train_images/"+image_id)
width,height=img.size
image_size.append((width,height))
train_image_size_df=pd.DataFrame(image_size,columns=["width","height"])
train_image_size_df.head()
print(train_image_size_df.width.unique())
print(train_image_size_df.height.unique())
from PIL import Image
image_size=[]
for image_id in listdir('test_images'):
img=Image.open("test_images/"+image_id)
width,height=img.size
image_size.append((width,height))
test_image_size_df=pd.DataFrame(image_size,columns=["width","height"])
test_image_size_df.head()
print(test_image_size_df.width.unique())
print(test_image_size_df.height.unique())
Therefore all images in train and test folders have the same size of (1600,256)
counts= train_df.ClassId.value_counts()
counts
my_colors = 'rgbkymc'
counts.plot(kind='bar')
plt.xlabel('Class')
plt.ylabel('Count')
plt.title('Image count per class')
plt.grid()
plt.show()
# ref: argsort https://docs.scipy.org/doc/numpy/reference/generated/numpy.argsort.html
# -(train_class_distribution.values): the minus sign will give us in decreasing order
sorted_counts = np.argsort(-counts.values)
for i in sorted_counts:
k=[3,1,4,2]
print('Number of images in class', k[i], ':',counts.values[i], '(', np.round((counts.values[i]/train_df.shape[0]*100), 3), '%)')
Observations:
#https://scikit-learn.org/stable/modules/generated/sklearn.utils.class_weight.compute_class_weight.html
from sklearn.utils import class_weight
class_wts = class_weight.compute_class_weight('balanced', np.unique(train_df.ClassId.values),train_df.ClassId)
print(class_wts)
for i in range(len(class_wts)):
print("Weight for class {}: {}".format(i+1,class_wts[i]))
Will be using these weights during training in the fit_generator function.
labels_per_image = train_df.groupby('ImageId')['ClassId'].count()
labels_per_image.value_counts()
my_colors = 'rgbkymc'
labels_per_image.value_counts().plot(kind='bar')
plt.xlabel('Class')
plt.ylabel('Count')
plt.title('Number of class labels per image')
plt.grid()
plt.show()
Observations:
d1= train_df[train_df.ClassId==1]
d2= train_df[train_df.ClassId==2]
d3= train_df[train_df.ClassId==3]
d4= train_df[train_df.ClassId==4]
d1.head()
# load all images in a directory
from matplotlib import image
loaded_images = list()
img_names= list(d1.ImageId.values)
for filename in listdir('train_images'):
if str(filename) in img_names:
# load image
img_data = image.imread('train_images/' + filename)
# store loaded image
loaded_images.append(img_data)
#displaying images
from matplotlib import pyplot
for i in range(5):
disp= loaded_images[i]
pyplot.imshow(disp)
pyplot.show()
# load all images in a directory
loaded_images = list()
img_names= list(d2.ImageId.values)
for filename in listdir('train_images'):
if str(filename) in img_names:
# load image
img_data = image.imread('train_images/' + filename)
# store loaded image
loaded_images.append(img_data)
#displaying images
from matplotlib import pyplot
for i in range(5):
disp= loaded_images[i]
pyplot.imshow(disp)
pyplot.show()
# load all images in a directory
loaded_images = list()
img_names= list(d3.ImageId.values)
for filename in listdir('train_images'):
if str(filename) in img_names:
# load image
img_data = image.imread('train_images/' + filename)
# store loaded image
loaded_images.append(img_data)
#displaying images
from matplotlib import pyplot
for i in range(5):
disp= loaded_images[i]
pyplot.imshow(disp)
pyplot.show()
# load all images in a directory
loaded_images = list()
img_names= list(d4.ImageId.values)
for filename in listdir('train_images'):
if str(filename) in img_names:
# load image
img_data = image.imread('train_images/' + filename)
# store loaded image
loaded_images.append(img_data)
#displaying images
from matplotlib import pyplot
for i in range(5):
disp= loaded_images[i]
pyplot.imshow(disp)
pyplot.show()
# preparing a dataframe with each image ID being represented with 4 classes
images= []
class_id= []
for img in listdir('train_images'):
images.append(img)
class_id.append(1)
images.append(img)
class_id.append(2)
images.append(img)
class_id.append(3)
images.append(img)
class_id.append(4)
train_images= pd.DataFrame(images,columns=['ImageId'])
train_images['ClassId'] = class_id
train_images.head()
train_images.shape
# Dataframe containing RLE(run length encoded pixels) representing defects
train_df.head()
# merging defect & non-defect data
temp_df = pd.merge(train_images, train_df,how='outer',on=['ImageId','ClassId'])
temp_df = temp_df.fillna('')
print(temp_df.shape)
temp_df.head()
# Grouping the data according to ImageID with each row representing images with single or multiple defects
#https://www.geeksforgeeks.org/python-pandas-pivot_table/
defect_data = pd.pivot_table(temp_df, values='EncodedPixels', index='ImageId',columns='ClassId', aggfunc=np.sum).astype(str)
defect_data = defect_data.reset_index()
defect_data.columns = ['ImageId','Defect_1','Defect_2','Defect_3','Defect_4']
defect_data.head()
defect_data.shape
from sklearn.model_selection import train_test_split
data= defect_data
train_data, cv_data = train_test_split(data, test_size=0.15)
print(train_data.shape)
print(cv_data.shape)
# to convert masks to run length encoded values
#https://www.kaggle.com/aleksandradeis/steel-defect-detection-eda
def mask2rle(img):
'''
img: numpy array, 1 - mask, 0 - background
Returns run length as string formated
'''
pixels= img.T.flatten()
pixels = np.concatenate([[0], pixels, [0]])
runs = np.where(pixels[1:] != pixels[:-1])[0] + 1
runs[1::2] -= runs[::2]
return ' '.join(str(x) for x in runs)
# to convert run length encoded pixels to masks
#https://www.kaggle.com/aleksandradeis/steel-defect-detection-eda
def rle2mask(rle):
'''
Returns mask array by converting run length encoded pixels
'''
# If rle is empty or null
if(len(rle)<1):
return np.zeros((128,800) ,dtype=np.uint8)
height = 256
width = 1600
# Defining the length of mask. This will be a 1d array which will be reshaped to 2d later.
mask = np.zeros(height*width ).astype(np.uint8)
array = np.asarray([int(x) for x in rle.split()]) # array containing rle
start = array[0::2]-1 # this willl contain the start of run length
length = array[1::2] # this will contain the length of each rle.
for i,start in enumerate(start):
mask[int(start):int(start+length[i])] = 1
return mask.reshape( (height,width), order='F' )[::2,::2]
from keras import backend as K
from keras.losses import binary_crossentropy
#https://lars76.github.io/neural-networks/object-detection/losses-for-segmentation/
#https://www.kaggle.com/wh1tezzz/correct-dice-metrics-for-this-competition
#https://forums.fast.ai/t/understanding-the-dice-coefficient/5838
def dice_coef(y_true, y_pred, smooth=1):
''' Function that returns dice coefficient by taking input masks and predicted masks'''
y_true_f = K.flatten(y_true)
y_pred_f = K.flatten(y_pred)
intersection = K.sum(y_true_f * y_pred_f)
return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)
def bce_dice_loss(y_true, y_predict): #combination of dice loss and binary cross entropy for all pixels
return binary_crossentropy(y_true, y_predict) + (1-dice_coef(y_true, y_predict))
from matplotlib import pyplot
def plot(history):
'''function to plot epoch vs bce_dice_loss & epoch vs dice_coeff '''
# plot bce_dice_loss
pyplot.subplot(121)
pyplot.title('bce_dice_loss')
pyplot.xlabel('Epoch')
pyplot.plot(history.history['loss'], color='blue', label='train')
pyplot.plot(history.history['val_loss'], color='orange', label='CV')
# plot dice_coeff
pyplot.subplot(122)
pyplot.title('Dice_coef')
pyplot.xlabel('Epoch')
pyplot.plot(history.history['dice_coef'], color='blue', label='train')
pyplot.plot(history.history['val_dice_coef'], color='orange', label='CV')
def visualize_defects(data,model):
''' Function that takes data containing imageID's ,model and outputs 4 masks for each image '''
import random
image_id= list(data.ImageId.values)
for i in random.sample(image_id, 5):
df= data[data.ImageId==i]
X = np.empty((1,128,800,3),dtype=np.float32)
img = cv2.imread('/content/' +'train_images/' + i)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (800,128))
X[0,] = img
mask_pred = model.predict(X)
fig, axs = plt.subplots(4,3,figsize=(18, 7))
axs[0,0].imshow(img)
axs[0,0].set_title(i)
for j in range(4):
if j<3:
axs[j+1,0].axis('off')
k=0
gt= rle2mask(df.iloc[0][j+1])
m = mask_pred[0,:,:,j].round().astype(int)
axs[j,k+1].imshow(gt)
axs[j,k+1].set_title('Ground truth mask: Class_'+str(j+1))
axs[j,k+2].imshow(m)
axs[j,k+2].set_title('Predicted mask: Class_'+str(j+1))
plt.show()
print('-'*100)
def visualize_defects_test(data,n):
''' Function that takes test ImageID's and the number of images to display as input and therefore outputs
mask with class ID'''
data= data[data['EncodedPixels']!='']
for i in random.sample(list(data['ImageId_ClassId'].values), n):
df= data[data.ImageId_ClassId==i]
img = Image.open('/content/' +'test_images/' + i.split('_')[0])
pixels= df.iloc[0][1]
mask= rle2mask(pixels)
fig, (ax1,ax2) = plt.subplots(1,2,figsize=(15, 7))
ax1.imshow(img)
ax1.set_title(i.split('_')[0])
ax2.imshow(mask)
ax2.set_title('Predicted mask with Class-'+i.split('_')[1]+' defect.')
plt.show()
print('-'*150)
for k in [1,2,3,4]: #classes
tmp = []
cnt=0
print("Sample images with Class {} defect:".format(k))
for i in train_data[train_data[f'Defect_{k}']!=''][['ImageId',f'Defect_{k}']].values:
if cnt<5:
fig, (ax1,ax2) = plt.subplots(nrows = 1,ncols = 2,figsize=(15, 7))
img = Image.open('train_images/' + str(i[0]))
ax1.imshow(img)
ax1.set_title(i[0])
cnt+=1
ax2.imshow(rle2mask(i[1]))
ax2.set_title(i[0]+'_mask_'+str(k))
plt.show()
print('-'*100)
# https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly
# https://keras.io/preprocessing/image/
import keras
from keras.preprocessing.image import ImageDataGenerator
class Train_DataGenerator(keras.utils.Sequence):
def __init__(self, df, batch_size = 16,shuffle=False,
preprocess=None, info={}):
super().__init__()
self.df = df
self.shuffle = shuffle
self.batch_size = batch_size
self.preprocess = preprocess
self.info = info
self.data_path = '/content/' + 'train_images/'
self.on_epoch_end()
def __len__(self):
return int(np.floor(len(self.df) / self.batch_size))
def on_epoch_end(self):
self.indexes = np.arange(len(self.df))
if self.shuffle == True:
np.random.shuffle(self.indexes)
def __getitem__(self, index):
train_datagen = ImageDataGenerator()
param = {'flip_horizontal':True, 'flip_vertical' : True}
''''
Performing data augmentation on images and the masks generated which includes Horizontal flip & Vertical flip
'''
X = np.empty((self.batch_size,128,800,3),dtype=np.float32) #images
y = np.empty((self.batch_size,128,800,4),dtype=np.int8) #masks
indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]
for i,f in enumerate(self.df['ImageId'].iloc[indexes]):
self.info[index*self.batch_size+i]=f
img = Image.open(self.data_path + f).resize((800,128))
X[i,] = train_datagen.apply_transform(x = img, transform_parameters = param)
#run-length encoding on the pixel values
for j in range(4):
mask= rle2mask(self.df['Defect_'+str(j+1)].iloc[indexes[i]])
y[i,:,:,j] = train_datagen.apply_transform(x = mask, transform_parameters = param)
if self.preprocess!=None: X = self.preprocess(X)
return X, y
# https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly
import keras
from keras.preprocessing.image import ImageDataGenerator
class Val_DataGenerator(keras.utils.Sequence):
def __init__(self, df, batch_size = 16,shuffle=False,
preprocess=None, info={}):
super().__init__()
self.df = df
self.shuffle = shuffle
self.batch_size = batch_size
self.preprocess = preprocess
self.info = info
self.data_path = '/content/' + 'train_images/'
self.on_epoch_end()
def __len__(self):
return int(np.floor(len(self.df) / self.batch_size))
def on_epoch_end(self):
self.indexes = np.arange(len(self.df))
if self.shuffle == True:
np.random.shuffle(self.indexes)
def __getitem__(self, index):
X = np.empty((self.batch_size,128,800,3),dtype=np.float32) #images
y = np.empty((self.batch_size,128,800,4),dtype=np.int8) #masks
indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]
for i,f in enumerate(self.df['ImageId'].iloc[indexes]):
self.info[index*self.batch_size+i]=f
X[i,] = Image.open(self.data_path + f).resize((800,128))
#run-length encoding on the pixel values
for j in range(4):
y[i,:,:,j] = rle2mask(self.df['Defect_'+str(j+1)].iloc[indexes[i]])
if self.preprocess!=None: X = self.preprocess(X)
return X, y
def conv2d_block(input_tensor, n_filters, kernel_size = 3, batchnorm = True):
"""Function to add 2 convolutional layers with the parameters passed to it"""
# first layer
x = Conv2D(filters = n_filters, kernel_size = (kernel_size, kernel_size),\
kernel_initializer = 'he_normal', padding = 'same')(input_tensor)
if batchnorm:
x = BatchNormalization()(x)
x = Activation('relu')(x)
# second layer
x = Conv2D(filters = n_filters, kernel_size = (kernel_size, kernel_size),\
kernel_initializer = 'he_normal', padding = 'same')(x)
if batchnorm:
x = BatchNormalization()(x)
x = Activation('relu')(x)
return x
def get_unet(input_img, n_filters, dropout, batchnorm):
"""Function to define the UNET architecture"""
# Contracting Path
c1 = conv2d_block(input_img, n_filters * 1, kernel_size = 3, batchnorm = batchnorm)
p1 = MaxPooling2D((2, 2))(c1)
p1 = Dropout(dropout)(p1)
c2 = conv2d_block(p1, n_filters * 2, kernel_size = 3, batchnorm = batchnorm)
p2 = MaxPooling2D((2, 2))(c2)
p2 = Dropout(dropout)(p2)
c3 = conv2d_block(p2, n_filters * 4, kernel_size = 3, batchnorm = batchnorm)
p3 = MaxPooling2D((2, 2))(c3)
p3 = Dropout(dropout)(p3)
c4 = conv2d_block(p3, n_filters * 8, kernel_size = 3, batchnorm = batchnorm)
p4 = MaxPooling2D((2, 2))(c4)
p4 = Dropout(dropout)(p4)
c5 = conv2d_block(p4, n_filters = n_filters * 16, kernel_size = 3, batchnorm = batchnorm)
# Expansive Path
u6 = Conv2DTranspose(n_filters * 8, (3, 3), strides = (2, 2), padding = 'same')(c5)
u6 = concatenate([u6, c4])
u6 = Dropout(dropout)(u6)
c6 = conv2d_block(u6, n_filters * 8, kernel_size = 3, batchnorm = batchnorm)
u7 = Conv2DTranspose(n_filters * 4, (3, 3), strides = (2, 2), padding = 'same')(c6)
u7 = concatenate([u7, c3])
u7 = Dropout(dropout)(u7)
c7 = conv2d_block(u7, n_filters * 4, kernel_size = 3, batchnorm = batchnorm)
u8 = Conv2DTranspose(n_filters * 2, (3, 3), strides = (2, 2), padding = 'same')(c7)
u8 = concatenate([u8, c2])
u8 = Dropout(dropout)(u8)
c8 = conv2d_block(u8, n_filters * 2, kernel_size = 3, batchnorm = batchnorm)
u9 = Conv2DTranspose(n_filters * 1, (3, 3), strides = (2, 2), padding = 'same')(c8)
u9 = concatenate([u9, c1])
u9 = Dropout(dropout)(u9)
c9 = conv2d_block(u9, n_filters * 1, kernel_size = 3, batchnorm = batchnorm)
outputs = Conv2D(4, (1, 1), activation='sigmoid')(c9)
model = Model(inputs=[input_img], outputs=[outputs])
return model
input_img = Input((128, 800, 3), name='img')
model = get_unet(input_img, n_filters=8, dropout=0.2, batchnorm=True)
model.compile(optimizer=Adam(), loss=bce_dice_loss, metrics=[dice_coef])
model.summary()
from keras.callbacks import ModelCheckpoint
from keras.callbacks import CSVLogger
import matplotlib.pyplot as plt
from tensorflow.python.keras.callbacks import TensorBoard
from keras.callbacks import TensorBoard
import tensorflow as tf
import datetime
import keras
from tensorboardcolab import *
from keras.callbacks import ReduceLROnPlateau
#https://github.com/taomanwai/tensorboardcolab/
tbc=TensorBoardColab()
#https://machinelearningmastery.com/check-point-deep-learning-models-keras/
mc = ModelCheckpoint('best_model_unet.h5', monitor='val_dice_coef', verbose=1, save_best_only=True, mode='max')
callbacks_list = [mc, TensorBoardColabCallback(tbc)]
train_batches = Train_DataGenerator(train_data,shuffle=True)
valid_batches = Val_DataGenerator(cv_data)
history = model.fit_generator(train_batches, validation_data = valid_batches, epochs = 50, verbose=1,
class_weight= class_wts,callbacks= callbacks_list) #class_wts as clculated in EDA
Please refer to Training Plots in utility functions section.
plot(history)
from keras.models import load_model
dependencies = {'bce_dice_loss':bce_dice_loss,'dice_coef':dice_coef,}
model_best = load_model('/content/best_model_unet.h5',custom_objects=dependencies)
evals= model_best.evaluate(valid_batches,verbose=1)
print('Validation set evaluation score:')
print('bce_dice loss:',evals[0])
print('dice_coeff:',evals[1])
Please refer to the "visualize_defects" function in Utility functions section.
visualize_defects(train_data,model_best)
visualize_defects(train_data,model_best)
visualize_defects(train_data,model_best)
visualize_defects(cv_data,model_best)
visualize_defects(cv_data,model_best)
visualize_defects(cv_data,model_best)
# Predicting on test images
from tqdm import tqdm
import cv2
data_path = '/content/' + 'test_images/'
files = list(os.listdir(data_path))
rle_lst = [] #list to store defect in run length encoding format
img_classId= [] #list to store Image ID + classId
for f in tqdm(files):
X = np.empty((1,128,800,3),dtype=np.float32)
img = cv2.imread(data_path + f)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (800,128))
X[0,] = img
mask = model_best.predict(X)
rle_m = np.empty((128,800),dtype=np.uint8)
for i in range(4):
rle_m = mask[0,:,:,i].round().astype(int)
rle = mask2rle(rle_m)
rle_lst.append(rle)
img_classId.append(f+'_'+str(i+1))
output = {'ImageId_ClassId':img_classId, 'EncodedPixels' : rle_lst}
import pandas as pd
output_df = pd.DataFrame(output)
output_df.to_csv('submission_unet_128X800.csv', index=False)
With this submission in kaggle, I got a private dice coefficient score of 0.79814 & a public score of 0.78045.
input_img = Input((256,1600,3), name='img')
model = get_unet(input_img, n_filters=8, dropout=0.2, batchnorm=True)
model.compile(optimizer=Adam(), loss=bce_dice_loss, metrics=[dice_coef])
model.summary()
model.set_weights(model_best.get_weights()) #transfering the weights of 128x800 model
# Predicting on test images
from tqdm import tqdm
import cv2
data_path = '/content/' + 'test_images/'
files = list(os.listdir(data_path))
rle_lst = [] #list to store defect in run length encoding format
img_classId= [] #list to store Image ID + classId
for f in tqdm(files):
X = np.empty((1,256,1600,3),dtype=np.float32)
img = cv2.imread(data_path + f)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
X[0,] = img
mask = model.predict(X)
rle_m = np.empty((256,1600),dtype=np.uint8)
for i in range(4):
rle_m = mask[0,:,:,i].round().astype(int)
rle = mask2rle(rle_m)
rle_lst.append(rle)
img_classId.append(f+'_'+str(i+1))
output = {'ImageId_ClassId':img_classId, 'EncodedPixels' : rle_lst}
import pandas as pd
output_df = pd.DataFrame(output)
output_df.to_csv('submission_unet_256_1600.csv', index=False)
With this submission, I got a private dice coefficient score of 0.81171 & a public score of 0.80041.
Please refer to the "visualize_defects_test" function in Utility functions section.
visualize_defects_test(output_df,5)
visualize_defects_test(output_df,5)
visualize_defects_test(output_df,5)
def two_conv2d_blocks(input_tensor, n_filters, kernel_size = 3, batchnorm = True):
"""Function containing 2 convolutional layers with the parameters passed to it"""
# first layer
x = Conv2D(filters = n_filters, kernel_size = (kernel_size, kernel_size),\
kernel_initializer = 'he_normal', padding = 'same')(input_tensor)
if batchnorm:
x = BatchNormalization()(x)
x = Activation('relu')(x)
# second layer
x = Conv2D(filters = n_filters, kernel_size = (kernel_size, kernel_size),\
kernel_initializer = 'he_normal', padding = 'same')(x)
if batchnorm:
x = BatchNormalization()(x)
x = Activation('relu')(x)
return x
def single_conv2d_block(input_tensor, n_filters, kernel_size = 3, batchnorm = True):
"""Function containing 1 convolutional layer with the parameters passed to it"""
# first layer
x = Conv2D(filters = n_filters, kernel_size = (kernel_size, kernel_size),\
kernel_initializer = 'he_normal', padding = 'same')(input_tensor)
if batchnorm:
x = BatchNormalization()(x)
x = Activation('relu')(x)
return x
def get_segnet(input_img, n_filters, dropout, batchnorm):
"""Function to define the Segnet architecture"""
# Encoder Path
c1 = two_conv2d_blocks(input_img, n_filters * 1, kernel_size = 3, batchnorm = batchnorm)
p1 = MaxPooling2D((2, 2))(c1)
p1 = Dropout(dropout)(p1)
c2 = two_conv2d_blocks(p1, n_filters * 2, kernel_size = 3, batchnorm = batchnorm)
p2 = MaxPooling2D((2, 2))(c2)
p2 = Dropout(dropout)(p2)
c3 = two_conv2d_blocks(p2, n_filters * 4, kernel_size = 3, batchnorm = batchnorm)
c3_1 = single_conv2d_block(c3, n_filters * 4, kernel_size = 3, batchnorm = batchnorm)
p3 = MaxPooling2D((2, 2))(c3_1)
p3 = Dropout(dropout)(p3)
c4 = two_conv2d_blocks(p3, n_filters * 8, kernel_size = 3, batchnorm = batchnorm)
c4_1= single_conv2d_block(c4, n_filters * 8, kernel_size = 3, batchnorm = batchnorm)
p4 = MaxPooling2D((2, 2))(c4_1)
p4 = Dropout(dropout)(p4)
c5 = two_conv2d_blocks(p4, n_filters = n_filters * 8, kernel_size = 3, batchnorm = batchnorm)
c5_1= single_conv2d_block(c5, n_filters * 8, kernel_size = 3, batchnorm = batchnorm)
p5 = MaxPooling2D((2, 2))(c5_1)
p5 = Dropout(dropout)(p5)
# Decoder Path
u6 = UpSampling2D()(p5)
c6 = two_conv2d_blocks(u6,n_filters * 8, kernel_size = 3, batchnorm = batchnorm)
c6 = single_conv2d_block(c6, n_filters * 8, kernel_size = 3, batchnorm = batchnorm)
c6 = Dropout(dropout)(c6)
u7 = UpSampling2D()(c6)
c7 = two_conv2d_blocks(u7,n_filters * 8, kernel_size = 3, batchnorm = batchnorm)
c7 = single_conv2d_block(c7, n_filters * 4, kernel_size = 3, batchnorm = batchnorm)
c7 = Dropout(dropout)(c7)
u8 = UpSampling2D()(c7)
c8 = two_conv2d_blocks(u8,n_filters * 4, kernel_size = 3, batchnorm = batchnorm)
c8 = single_conv2d_block(c8, n_filters * 2, kernel_size = 3, batchnorm = batchnorm)
c8 = Dropout(dropout)(c8)
u9 = UpSampling2D()(c8)
c9 = single_conv2d_block(u9,n_filters * 2, kernel_size = 3, batchnorm = batchnorm)
c9 = single_conv2d_block(c9, n_filters * 1, kernel_size = 3, batchnorm = batchnorm)
c9 = Dropout(dropout)(c9)
u10 = UpSampling2D()(c9)
c10 = single_conv2d_block(u10,n_filters * 1, kernel_size = 3, batchnorm = batchnorm)
c10 = Dropout(dropout)(c10)
outputs = Conv2D(4, (1, 1), activation='sigmoid')(c10)
model = Model(inputs=[input_img], outputs=[outputs])
return model
input_img = Input((128,800,3),name='img')
model = get_segnet(input_img, n_filters=16, dropout=0.5, batchnorm=True)
model.compile(optimizer=Adam(), loss=bce_dice_loss, metrics=[dice_coef])
model.summary()
tbc=TensorBoardColab()
mc = ModelCheckpoint('best_model_segnet.h5', monitor='val_dice_coef', verbose=1, save_best_only=True, mode='max')
callbacks_list = [mc, TensorBoardColabCallback(tbc)]
train_batches = Train_DataGenerator(train_data,shuffle=True)
valid_batches = Val_DataGenerator(cv_data)
history = model.fit_generator(train_batches, validation_data = valid_batches, epochs = 30, verbose=1,
class_weight=class_wts, callbacks = callbacks_list)
train_batches = Train_DataGenerator(train_data,shuffle=True)
valid_batches = Val_DataGenerator(cv_data)
history = model.fit_generator(train_batches, validation_data = valid_batches, epochs = 20, verbose=1,
class_weight=class_wts, callbacks = callbacks_list)
Please refer to Training Plots in utility functions section.
#first 30 epochs
plot(history)
#next 20 epochs
plot(history)
Considering the best model which was trained on 30 epochs as the model in the next 20 epochs experienced the problem of overfitting.
from keras.models import load_model
dependencies = {'bce_dice_loss':bce_dice_loss,'dice_coef':dice_coef,}
model_best = load_model('/content/best_model_segnet.h5',custom_objects=dependencies)
evals= model_best.evaluate(valid_batches,verbose=1)
print('Validation set evaluation score:')
print('bce_dice loss:',evals[0])
print('dice_coeff:',evals[1])
Please refer to the "visualize_defects" function in Utility functions section.
visualize_defects(train_data,model_best)
visualize_defects(train_data,model_best)
visualize_defects(cv_data,model_best)
visualize_defects(cv_data,model_best)
input_img = Input((256,1600,3),name='img')
model1 = get_segnet(input_img, n_filters=16, dropout=0.5, batchnorm=True)
model1.compile(optimizer=Adam(), loss=bce_dice_loss, metrics=[dice_coef])
model1.summary()
model1.set_weights(model_best.get_weights())
# Predicting on test images
from tqdm import tqdm
import cv2
data_path = '/content/' + 'test_images/'
files = list(os.listdir(data_path))
rle_lst = [] #list to store defect in run length encoding format
img_classId= [] #list to store Image ID + classId
for f in tqdm(files):
X = np.empty((1,256,1600,3),dtype=np.float32)
img = cv2.imread(data_path + f)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
X[0,] = img
mask = model1.predict(X)
rle_m = np.empty((256,1600),dtype=np.uint8)
for i in range(4):
rle_m = mask[0,:,:,i].round().astype(int)
rle = mask2rle(rle_m)
rle_lst.append(rle)
img_classId.append(f+'_'+str(i+1))
output = {'ImageId_ClassId':img_classId, 'EncodedPixels' : rle_lst}
output_df = pd.DataFrame(output)
output_df.to_csv('submission_segnet_256x1600.csv', index=False)
With this submission, I got a private dice coefficient score of 0.83437 & a public score of 0.84740.
Please refer to the "visualize_defects_test" function in Utility functions section.
visualize_defects(output_df,5)
visualize_defects(output_df,5)
visualize_defects(output_df,5)
visualize_defects(output_df,5)
visualize_defects(output_df,5)
def fcn_8(img_shape, base, dropout,n_classes):
i = Input(shape=img_shape)
b= base
## Block 1
x = Conv2D(2**b, (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block1_conv1')(i)
x = BatchNormalization()(x)
x = Conv2D(2**b, (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block1_conv2')(x)
x = BatchNormalization()(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x)
x = Dropout(dropout)(x)
# Block 2
x = Conv2D(2**(b+1), (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block2_conv1')(x)
x = BatchNormalization()(x)
x = Conv2D(2**(b+1), (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block2_conv2')(x)
x = BatchNormalization()(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(x)
x = Dropout(dropout)(x)
# Block 3
x = Conv2D(2**(b+2), (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block3_conv1')(x)
x = BatchNormalization()(x)
x = Conv2D(2**(b+2), (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block3_conv2')(x)
x = BatchNormalization()(x)
x = Conv2D(2**(b+2), (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block3_conv3')(x)
x = BatchNormalization()(x)
x = MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')(x)
x = Dropout(dropout)(x)
pool3 = x
# Block 4
x = Conv2D(2**(b+3), (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block4_conv1')(x)
x = BatchNormalization()(x)
x = Conv2D(2**(b+3), (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block4_conv2')(x)
x = BatchNormalization()(x)
x = Conv2D(2**(b+3), (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block4_conv3')(x)
x = BatchNormalization()(x)
pool4 = MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')(x)
pool4 = Dropout(dropout)(pool4)
# Block 5
x = Conv2D(2**(b+3), (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block5_conv1')(pool4)
x = BatchNormalization()(x)
x = Conv2D(2**(b+3), (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block5_conv2')(x)
x = BatchNormalization()(x)
x = Conv2D(2**(b+3), (3, 3), activation='relu', kernel_initializer = 'he_normal', padding='same', name='block5_conv3')(x)
x = BatchNormalization()(x)
pool5 = MaxPooling2D((2, 2), strides=(2, 2), name='block5_pool')(x)
pool5 = Dropout(dropout)(pool5)
conv6 = Conv2D(2**(b+3)*8 , (7, 7) , activation='relu', kernel_initializer = 'he_normal' , padding='same', name="conv6")(pool5)
conv6 = BatchNormalization()(conv6)
conv6 = Dropout(0.5)(conv6)
conv7 = Conv2D(2**(b+3)*8 , (1, 1) , activation='relu', kernel_initializer = 'he_normal' , padding='same', name="conv7")(conv6)
conv7 = BatchNormalization()(conv7)
conv7 = Dropout(0.5)(conv7)
pool4_n = Conv2D(n_classes, (1, 1), activation='relu', kernel_initializer = 'he_normal', padding='same')(pool4)
u2 = Conv2DTranspose(n_classes, kernel_size=(2, 2), strides=(2, 2), padding='same')(conv7)
u2_skip = Add()([pool4_n, u2])
pool3_n = Conv2D(n_classes, (1, 1), activation='relu', padding='same')(pool3)
u4 = Conv2DTranspose(n_classes, kernel_size=(2, 2), strides=(2, 2), padding='same')(u2_skip)
u4_skip = Add()([pool3_n, u4])
output = Conv2DTranspose(n_classes, kernel_size=(8, 8), strides=(8, 8), padding='same',activation='sigmoid')(u4_skip)
model = Model(inputs=[i], outputs=[output])
return model
#https://keras.io/models/model/#fit_generator
model= fcn_8(img_shape= (128,800,3), base= 4, dropout= 0.3, n_classes= 4)
model.compile(optimizer=Adam(), loss=bce_dice_loss, metrics=[dice_coef])
model.summary()
tbc=TensorBoardColab()
mc = ModelCheckpoint('best_model_fcn8.h5', monitor='val_dice_coef', verbose=1, save_best_only=True, mode='max')
callbacks_list = [mc, TensorBoardColabCallback(tbc)]
train_batches = Train_DataGenerator(train_data,shuffle=True)
valid_batches = Val_DataGenerator(cv_data)
history = model.fit_generator(train_batches, validation_data = valid_batches, epochs = 30, verbose=1,
class_weight=class_wts, callbacks = callbacks_list)
#round 2 for 20 epochs
model= model_best
train_batches = Train_DataGenerator(train_data,shuffle=True)
valid_batches = Val_DataGenerator(cv_data)
class_wts= [1.98,7.18,0.34,2.21]
history = model.fit_generator(train_batches, validation_data = valid_batches, epochs = 20, verbose=1,
class_weight=class_wts, callbacks = callbacks_list)
Please refer to Training Plots in utility functions section.
#first 30 epochs
plot(history)
#next 20 epochs
plot(history)
Considering the best model which was trained on 30 epochs as the model in the next 20 epochs experienced the problem of overfitting.
from keras.models import load_model
dependencies = {'bce_dice_loss':bce_dice_loss,'dice_coef':dice_coef,}
model_best = load_model('/content/best_model_fcn8.h5',custom_objects=dependencies)
evals1= model_best.evaluate(valid_batches,verbose=1)
print('Validation set evaluation score:')
print('bce_dice loss:',evals1[0])
print('dice_coeff:',evals1[1])
Please refer to the "visualize_defects" function in Utility functions section.
visualize_defects(train_data,model_best)
visualize_defects(train_data,model_best)
visualize_defects(cv_data,model_best)
visualize_defects(cv_data,model_best)
#https://keras.io/models/model/#fit_generator
model= fcn_8(img_shape= (256,1600,3), base= 4, dropout= 0.3, n_classes= 4)
model.compile(optimizer=Adam(), loss=bce_dice_loss, metrics=[dice_coef])
model.summary()
model.set_weights(model_best.get_weights())
# Predicting on test images
from tqdm import tqdm
import cv2
data_path = '/content/' + 'test_images/'
files = list(os.listdir(data_path))
rle_lst = [] #list to store defect in run length encoding format
img_classId= [] #list to store Image ID + classId
for f in tqdm(files):
X = np.empty((1,256,1600,3),dtype=np.float32)
img = cv2.imread(data_path + f)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
X[0,] = img
mask = model.predict(X)
rle_m = np.empty((256,1600),dtype=np.uint8)
for i in range(4):
rle_m = mask[0,:,:,i].round().astype(int)
rle = mask2rle(rle_m)
rle_lst.append(rle)
img_classId.append(f+'_'+str(i+1))
output = {'ImageId_ClassId':img_classId, 'EncodedPixels' : rle_lst}
import pandas as pd
output_df = pd.DataFrame(output)
output_df.to_csv('submission_fcn8_256x1600.csv', index=False)
With this submission, I got a private dice coefficient score of 0.82818 & a public score of 0.84935.
Please refer to the "visualize_defects_test" function in Utility functions section.
visualize_defects_test(output_df,5)
visualize_defects_test(output_df,5)
visualize_defects_test(output_df,5)
#Ref: http://zetcode.com/python/prettytable/
from prettytable import PrettyTable
x = PrettyTable()
x.field_names = ["Architecture","Test Dice_coeff-Private","Test Dice_coeff-Public"]
x.add_row(["UNET",0.81171,0.80041])
x.add_row(["SEGNET",0.83437,0.84741])
x.add_row(["FCN-8",0.82818,0.84935])
print(x)